/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

package com.huawei.housekeeper.config;

import com.huawei.housekeeper.model.AdminContext;
import com.huawei.housekeeper.model.ListAdminContext;
import com.huawei.housekeeper.config.filter.JwtAuthenticationTokenFilter;
import com.huawei.housekeeper.config.filter.RestAuthenticationEntryPoint;
import com.huawei.housekeeper.config.filter.RestfulAccessDeniedHandler;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;

import java.util.List;

/**
 * spring security
 *
 * @author y00464350
 * @since 2022-02-11
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Log4j2
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;

    @Autowired
    private ListAdminContext adminContext;

    private static final String[] EXCLUDED_AUTHPAGES = {"/tenant/identifyCode", "/tenant/getEmail", "/admin/login", "/doc.html", "/webjars/js/**",
            "/webjars/css/**", "/webjars/**", "/swagger-resources/**", "/v2/api-docs/**"};

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
                // 由于使用的是JWT，我们这里不需要csrf{
                .disable()
                .sessionManagement() // 基于token，所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**")
                .permitAll()
                .antMatchers(EXCLUDED_AUTHPAGES)
                .permitAll()
                .antMatchers("/tenant/**")
                .permitAll()
                .anyRequest()
                .authenticated();
        http.headers().cacheControl();

        // 添加JWT filter
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        // 添加自定义未授权和未登录结果返回
        http.exceptionHandling()
                .authenticationEntryPoint(restAuthenticationEntryPoint)
                .accessDeniedHandler(restfulAccessDeniedHandler);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        List<AdminContext> adminContextList = this.adminContext.getAdminContext();
        // 管理员信息以内存生成
        adminContextList.forEach(s -> {
            try {
                auth.inMemoryAuthentication()
                        .withUser(s.getName())
                        .password(passwordEncoder().encode(s.getPassword()))
                        .roles(s.getRole());
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        });


    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置地址栏不能识别 // 的情况
     *
     * @return
     */
    @Bean
    public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
        StrictHttpFirewall firewall = new StrictHttpFirewall();

        // 此处可添加别的规则,目前只设置 允许双 //
        firewall.setAllowUrlEncodedDoubleSlash(true);
        return firewall;
    }
}
